package com.zjtx.blog.filter;

import com.zjtx.blog.service.RedisSecurityService;
import com.zjtx.blog.service.SecurityAnalyzerService;
import com.zjtx.blog.utils.IpUtil;
import com.zjtx.blog.utils.SecurityConstants;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.util.ContentCachingRequestWrapper;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.regex.Pattern;

@Component
@Order(1)
public class RedisSecurityFilter implements Filter {
    
    private static final Logger logger = LoggerFactory.getLogger(RedisSecurityFilter.class);
    
    @Autowired
    private RedisSecurityService redisSecurityService;
    
    @Autowired
    private SecurityAnalyzerService securityAnalyzerService;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.debug("-----------------------------RedisSecurityFilter INIT-----------------------------");
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        ContentCachingRequestWrapper wrapperRequest = new ContentCachingRequestWrapper(httpRequest);
        if(!performSecurityCheck(wrapperRequest, httpResponse)) {
            return;
        }
        chain.doFilter(wrapperRequest, response);
    }

    private boolean performSecurityCheck(ContentCachingRequestWrapper httpRequest,
                                         HttpServletResponse httpResponse) throws IOException {
        String clientIP = getClientIP(httpRequest);

        if(Pattern.matches(SecurityConstants.IP_LOCALHOST_REGEX, clientIP)) {
            logger.warn("detected local call, will not block: {}", clientIP);
            return true;
        }

        String region = IpUtil.getIp2region(clientIP);
        if(StringUtils.isNotEmpty(region) && !region.startsWith("中国")) {
            logger.warn("detected call from foreign country, will add blacklist: {}, {}", clientIP, region);
            redisSecurityService.blacklistIp(clientIP, "country not supported:" + region);
            httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
            httpResponse.getWriter().write("Access Denied");
            return false;
        }

        String userAgent = httpRequest.getHeader("User-Agent");
        String requestURI = httpRequest.getRequestURI();
        String method = httpRequest.getMethod();
        String queryString = httpRequest.getQueryString();

        // 构造完整的请求路径用于分析
        String fullPath = requestURI + (queryString != null ? "?" + queryString : "");

        // 1. 检查IP黑名单
        if (redisSecurityService.isIpBlacklisted(clientIP)) {
            logger.warn("Blocked request from blacklisted IP: {} for URI: {}", clientIP, fullPath);
            httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
            httpResponse.getWriter().write("Access Denied");
            return false;
        }

        // 2. 检查恶意User-Agent
        if (redisSecurityService.isMaliciousUserAgent(userAgent)) {
            logger.warn("Blocked request with malicious User-Agent: {} from IP: {}", userAgent, clientIP);
            redisSecurityService.blacklistIp(clientIP, "Malicious User-Agent: " + userAgent);
            httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
            httpResponse.getWriter().write("Access Denied");
            return false;
        }

        // 3. 检查恶意路径
        if (redisSecurityService.isMaliciousPath(requestURI)) {
            logger.warn("Blocked request to malicious path: {} from IP: {}", fullPath, clientIP);
            redisSecurityService.blacklistIp(clientIP, "Malicious path access: " + fullPath);
            httpResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
            httpResponse.getWriter().write("Not Found");
            return false;
        }

        // 4. 检查请求内容中的恶意代码
        if (securityAnalyzerService.containsMaliciousContent(httpRequest)) {
            logger.warn("Blocked request with malicious content from IP: {} for URI: {}", clientIP, fullPath);
            redisSecurityService.blacklistIp(clientIP, "Malicious content detected");
            httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            httpResponse.getWriter().write("Bad Request");
            return false;
        }

        // 5. 检查频率限制
        if (redisSecurityService.isRateLimited(clientIP)) {
            logger.warn("Rate limit exceeded for IP: {} on URI: {}", clientIP, fullPath);
            redisSecurityService.blacklistIp(clientIP, "Rate limit exceeded");
            httpResponse.setStatus(SecurityConstants.SC_TOO_MANY_REQUESTS);
            httpResponse.getWriter().write("Too Many Requests");
            return false;
        }

        // 6. 检查不常见的HTTP方法
        if (!isAllowedMethod(method)) {
            logger.warn("Blocked request with disallowed method: {} from IP: {}", method, clientIP);
            redisSecurityService.blacklistIp(clientIP, "Disallowed HTTP method: " + method);
            httpResponse.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
            httpResponse.getWriter().write("Method Not Allowed");
            return false;
        }
        return true;
    }


    private boolean isAllowedMethod(String method) {
        return "GET".equals(method) || "POST".equals(method)
                || "HEAD".equals(method) || "PUT".equals(method)
                || "DELETE".equals(method);
    }
    
    private String getClientIP(HttpServletRequest request) {
        String xfHeader = request.getHeader("X-Forwarded-For");
        if (xfHeader == null) {
            return request.getRemoteAddr();
        }
        return xfHeader.split(",")[0].trim();
    }
}
